package test;

import static org.junit.Assert.*;

import java.util.Vector;

import bgai.ai.minimax.MoveEvent;
import bgai.ai.minimax.Search;
import bgai.gamelogic.Backgammon;
import bgai.gamelogic.BadMoveException;
import bgai.gamelogic.CheckerColor;
import bgai.gamelogic.Move;
import bgai.gamelogic.SingleMove;
import bgai.players.DoorHit2;
import bgai.players.Player;

/**
 * Ad ogni turno eseguo lo star1 e lo star2 e confronta le mosse restituite.
 * Si ferma se le mosse sono discrepanti.
 */
public class BugFinder {

	@org.junit.Test
	public void find() throws BadMoveException {
		
		int seed= 2;
		final int depth= 3;
		
		Player black= new DoorHit2(CheckerColor.BLACK, depth);
		Player white= new DoorHit2(CheckerColor.WHITE, depth);
		
		Backgammon bg= new Backgammon(black, white, seed);
		bg.setSilent(true); // <------------- output classe Backgammon
		
		Player turn= tieBreak(black, white, bg);
		boolean win= false;
		
		Search.setSilentDebug(true); // <-------- output classe Search
		Search.DISABLE_PROBING_AB= false;
		Search.DISABLE_PROBING_CUTOFF= false;
		Search.FAIL_SOFT= true; // fail hard
		
		System.out.println("DISABLE_PROBING_AB="+Search.DISABLE_PROBING_AB);
		System.out.println("DISABLE_PROBING_CUTOFF="+Search.DISABLE_PROBING_CUTOFF);
		System.out.println("FAIL_SOFT="+Search.FAIL_SOFT);
		System.out.println("depth(black)="+depth);
		System.out.println("depth(white)="+depth);
		System.out.println("seed="+seed);
		System.out.println();
		
		int countTurns=0;
		int numTurns=Integer.MAX_VALUE;
		while (!win) {
			countTurns++;
			
			System.out.println("+ "+bg);
			
			// lancio i dadi
			int dice1= bg.rollDice();
			int dice2= bg.rollDice();
			bg.setRoll(dice1, dice2);
			
			// cerco la mossa
			System.out.println("> Ricerca mossa con expectimax");
			Move moveExp= null;
			//moveExp= (Move) Search.bestMoveExpectimax(turn, bg, depth);
			System.out.println("> Miglior mossa per expectimax: "+moveExp);
			
			System.out.println("> Ricerca mossa con star1");
			Search.STAR2= false;
			Move moveStar1= null;
			//moveStar1= (Move) Search.bestMoveStar(turn, bg, depth);
			System.out.println("> Miglior mossa per star1: "+moveStar1);
			
			System.out.println("> Ricerca mossa con star2");
			Search.STAR2= true;
			Move moveStar2= null;
			moveStar2= (Move) Search.bestMoveStar(turn, bg, depth);
			System.out.println("> Miglior mossa per star2: "+moveStar2);
			
			// XXX XXX XXX
			/*
			System.out.println("Mossa per expectimax: "+moveExp);
			System.out.println("Mossa per star1: "+moveStar1);
			System.out.println("Mossa per star2: "+moveStar2);
			if (true) break;
			*/
			
			if (
					false 
					/*!moveStar1.equals(moveStar2)*/ 
					/*|| !moveStar1.equals(moveExp)*/
					) {
				// mosse diverse!
				System.out.println("ERRORE: MOSSE DISCREPANTI");
				System.out.println("Mossa per expectimax: "+moveExp);
				System.out.println("Mossa per star1: "+moveStar1);
				System.out.println("Mossa per star2: "+moveStar2);
				System.out.println("Stato del gioco:");
				System.out.println(bg.fullStateToString());
				fail("Mosse discrepanti");
			} else {
				System.out.println("> Le mosse coincidono");
			}
			
			// eseguo la mossa
			win= bg.move(moveStar2);
			
			// cambio giocatore
			if (!win) {
				CheckerColor color= bg.tooglePlayerTurn();
				if (color.isBlack()) turn= black;
				else turn= white;
			}
			System.out.println("\n\n-------------------------------\n\n");
			
			// forzo interruzione do numTurns turni
			if (countTurns>=numTurns) break;
		}
		
		System.out.println("Stato finale:");
		System.out.println(bg.fullStateToString());
		
	}
	

	@org.junit.Test
	public void find2() throws BadMoveException {
		
		int seed= 2;
		final int depth= 3;
		
		Search.DISABLE_PROBING_AB= false;
		Search.DISABLE_PROBING_CUTOFF= false;
		Search.FAIL_SOFT= false; // fail hard
		Search.STAR2= true;
		
		System.out.println("DISABLE_PROBING_AB="+Search.DISABLE_PROBING_AB);
		System.out.println("DISABLE_PROBING_CUTOFF="+Search.DISABLE_PROBING_CUTOFF);
		System.out.println("FAIL_SOFT="+Search.FAIL_SOFT);
		System.out.println("STAR2="+Search.STAR2);
		System.out.println("depth(black)="+depth);
		System.out.println("depth(white)="+depth);
		System.out.println("seed="+seed);
		System.out.println();
		
		Player black= new DoorHit2(CheckerColor.BLACK, depth);
		Player white= new DoorHit2(CheckerColor.WHITE, depth);
		
		Backgammon bgBW= new Backgammon(black, white, seed);
		bgBW.setSilent(true); // <------------- output classe Backgammon
		Backgammon bgWB= new Backgammon(white, black, seed); // in realta' non cambia niente nell'invertire l'ordine qui
		bgWB.setSilent(true); // <------------- output classe Backgammon
		
		Player turnBW= tieBreak(black, white, bgBW);
		Player turnWB= tieBreak(white, black, bgWB);
		
		boolean win= false;
		int countTurns=0;
		int numTurns=Integer.MAX_VALUE;
		while (!win) {
			countTurns++;
			
			System.out.println("+ "+bgBW);
			
			// lancio i dadi
			int dice1= bgBW.rollDice();
			int dice2= bgBW.rollDice();
			bgBW.setRoll(dice1, dice2);
			bgWB.setRoll(dice1, dice2);
		
			
			// cerco la mossa
			System.out.println("> Ricerca mossa per black VS white");
			Search.setSilentDebug(true); // <-------- output classe Search
			Search.setSilent(false); // <-------- output classe Search
			Move moveBW= (Move) Search.bestMoveStar(turnBW, bgBW, depth);
			System.out.println("> Miglior mossa per black VS white: "+moveBW);
			
			System.out.println("> Ricerca mossa per white VS white");
			Search.setSilentDebug(true); // <-------- output classe Search
			Search.setSilent(false); // <-------- output classe Search
			Move moveWB= (Move) Search.bestMoveStar(turnWB, bgWB, depth);
			System.out.println("> Miglior mossa per black VS white: "+moveWB);
			
			// verifico
			if (!checkComplementari(moveBW, moveWB)) {
				
				System.out.println();
				System.out.println("BLACK vs WHITE");
				System.out.println("Mosse disponibili:");
				for (MoveEvent me : bgBW.moveEvents(turnBW)) {
					Move m= (Move) me;
					System.out.println(m);
				}
				System.out.println("Mossa BW: "+moveBW);
				
				System.out.println();
				System.out.println("WHITE vs BLACK");
				System.out.println("Mosse disponibili:");
				for (MoveEvent me : bgWB.moveEvents(turnWB)) {
					Move m= (Move) me;
					System.out.println(m);
				}
				System.out.println("Mosse complementari disponibili:");
				for (MoveEvent me : bgWB.moveEvents(turnWB)) {
					Move m= (Move) me;
					System.out.println(complementare(m));
				}
				System.out.println("Mossa WB: "+moveWB);
				System.out.println("Mossa WB complementare: "+complementare(moveWB));
				
				
				fail("Trovata discrepanza");
			}
			
			// eseguo la mossa
			win= bgBW.move(moveBW);
			bgWB.move(moveWB);
			
			// cambio giocatore
			if (!win) {
				CheckerColor color;
				
				color= bgBW.tooglePlayerTurn();
				if (color.isBlack()) turnBW= black;
				else turnBW= white;
				
				color= bgWB.tooglePlayerTurn();
				if (color.isBlack()) turnWB= black;
				else turnWB= white;
				
				if (turnBW.equals(turnWB)) fail("Stesso colore");
			}
			System.out.println("\n\n-------------------------------\n\n");
			
			// forzo interruzione do numTurns turni
			if (countTurns>=numTurns) break;
		}
		
		System.out.println("Stato finale BW:");
		System.out.println(bgBW.fullStateToString());
		System.out.println();
		System.out.println("Stato finale WB:");
		System.out.println(bgWB.fullStateToString());

		
		
	}
	
	Move complementare(Move m) {

		CheckerColor color;
		if (m.getColor().isBlack()) color= CheckerColor.WHITE;
		else color= CheckerColor.BLACK;
		
		Move comp= new Move(m.getHighDice(), m.getLowDice(), color);
		for (SingleMove sm : m) {
			try {
				comp.add(complementare(sm));
			} catch (BadMoveException e) {
				e.printStackTrace();
				fail();
			}
		}
		return comp;
	}
	
	SingleMove complementare(SingleMove sm) {

		// calcolo il complementare
		
		int startPos= sm.getStartingPosition();
		if (Backgammon.isOnTable(startPos)) startPos= 25-startPos;
		
		int endPos= sm.getEndingPosition();
		if (Backgammon.isOnTable(endPos)) endPos= 25-endPos;
		
		CheckerColor color;
		if (sm.getColor().isBlack()) color= CheckerColor.WHITE;
		else color= CheckerColor.BLACK;
		
		SingleMove comp= new SingleMove(startPos, endPos, sm.getDice(), color);
		comp.setHit(sm.isHit());
		
		return comp;
	}
	
	boolean checkComplementari(Move move1, Move move2) {
		
		Move black, white;
		
		if (move1.getColor().isBlack()) {
			black= move1;
			if (move2.getColor().isBlack()) throw new RuntimeException();
			white= move2;
		} else {
			white= move1;
			if (move2.getColor().isWhite()) throw new RuntimeException();
			black= move2;
		}
		Vector<SingleMove> blackArr= new Vector<SingleMove>();
		Vector<SingleMove> whiteArr= new Vector<SingleMove>();
		
		for (SingleMove sm : black) {
			blackArr.add(sm);
		}
		for (SingleMove sm : white) {
			whiteArr.add(complementare(sm));
		}
		
		return blackArr.equals(whiteArr);
		
	}

	Player tieBreak(Player one, Player two, Backgammon bg) {
		
		int dice1, dice2;
		
		// dadi dello spareggio
		dice1= bg.rollDice();
		dice2= bg.rollDice();
		
		if(dice1==dice2){
			// stesso valore, lo spareggio deve essere rieseguito
			return tieBreak(one, two, bg); // nessuna iterazione ma solo ricorsione!!!
		}

		Player first= null;

		if(dice1 < dice2){
			first=two; 
		} else if (dice2 < dice1){
			first=one;
		}

		// imposto chi inizia il turno
		bg.setPlayerTurn(first.getColor());

		Search.resetStats();
		
		return first;
	}
	
}
